home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Graphics / Gnuplot / Source / PlotView.m < prev    next >
Encoding:
Text File  |  1993-03-18  |  11.5 KB  |  535 lines

  1.  
  2. static char RCSId[]="$Id: PlotView.m,v 1.1.1.1 1993/03/18 03:34:58 davis Exp $";
  3.  
  4.  
  5. #import <appkit/Application.h>
  6. #import <appkit/ClipView.h>
  7. #import <appkit/Font.h>
  8. #import <appkit/FontManager.h>
  9. #import <appkit/Matrix.h>
  10. #import <appkit/MenuCell.h>
  11. #import <appkit/NXImage.h>
  12. #import <appkit/Pasteboard.h>
  13. #import <appkit/SavePanel.h>
  14. #import <appkit/Text.h>
  15.  
  16. #import <defaults/defaults.h>    /* NXGetTempFilename()    */
  17.  
  18. #import <dpsclient/psops.h>
  19. #import <libc.h>        /* unlink(), MAXPATHLEN    */
  20.  
  21. #import "GnuplotPlot.h"
  22. #import "PlotView.h"
  23.  
  24.  
  25.  
  26. /*  Does the list of NXAtoms "types" include the NXAtom "type"?  */
  27. static BOOL includesType(NXAtom *types, NXAtom type)
  28. {
  29.     while (types && *types) {
  30.         if (type == *types) return YES;
  31.         types++;
  32.     }
  33.     return NO;
  34. }
  35.  
  36.  
  37. @implementation PlotView
  38.  
  39.  
  40. - initFrame:(const NXRect *)frameRect;
  41. {
  42.     const char *validSendTypes[2];
  43.  
  44.     [super initFrame:frameRect];
  45.     [self setClipping: NO];    /* Because we're in a ClipView already    */
  46.     [self drawInSuperview];    /* Use our ClipView's coordinate system    */
  47.  
  48.     fullPath = NULL;
  49.     epsStream = NULL;
  50.     image = nil;
  51.     tempFileNeedsUpdate = YES;
  52.  
  53.     /* Setup services */
  54.     validSendTypes[0] = NXPostScriptPboardType;
  55.     validSendTypes[1] = NXTIFFPboardType;
  56.     validSendTypes[2] = NULL;
  57.     [NXApp registerServicesMenuSendTypes: validSendTypes andReturnTypes: NULL];
  58.  
  59.     return self;
  60. }
  61.  
  62.  
  63. - (BOOL) acceptsFirstResponder
  64. {
  65.     return YES;            /* So the font can be changed with fontpanel */
  66. }
  67.  
  68.  
  69. - (BOOL)acceptsFirstMouse
  70. {
  71.     return YES;            /* So we can drag the EPS out as a file    */
  72. }
  73.  
  74.  
  75.  
  76. - changeFont:sender
  77. {
  78.     id newfont;
  79.     id doc;
  80.  
  81.     newfont = [sender convertFont: [(doc = [window delegate]) currentFont]];
  82.     [doc setCurrentFont:newfont];
  83.  
  84.     return self;
  85. }
  86.  
  87.  
  88. - copyFont:sender
  89. {
  90.     /* 
  91.      *  This method is invoked when a user presses the "Copy Font" 
  92.      *  menu button.  We're supposed to put the current font on the 
  93.      *  font pasteboard, and the easiest way to do that is to take 
  94.      *  advantage of the Text class's ability to do that.  Every step 
  95.      *  here is necessary:  we create a Text object, set it to RTF 
  96.      *  (non-monofont), make a selection (even though there is no text 
  97.      *  to select, there must be a selection), set the font to the 
  98.      *  doc's current font, tell the text object to copy the font to 
  99.      *  the font pasteboard, and then free the text object.
  100.      */
  101.     [[[[[[[Text allocFromZone: [self zone]] init]
  102.           setMonoFont: NO]
  103.                setSel: 0:1]
  104.               setFont: [[window delegate] currentFont]]
  105.              copyFont: sender]
  106.                  free];
  107.  
  108.     return self;
  109. }
  110.  
  111.  
  112. - pasteFont:sender
  113. {
  114.     Text *text = [[Text allocFromZone: [self zone]] init];
  115.  
  116.     /* 
  117.      *  We must specify that the text is not monofont (i.e. is RTF) 
  118.      *  and there must be a selection in order to paste.  There 
  119.      *  doesn't need to be any text.
  120.      */
  121.     [[text setMonoFont:NO] setSel:0 :1];
  122.  
  123.     if ([text pasteFont:sender])    /* Returns nil if there's trouble */
  124.     [[window delegate] setCurrentFont:[text font]];
  125.  
  126.     [text free];
  127.     return self;
  128. }
  129.  
  130.  
  131.  
  132. - validRequestorForSendType:(NXAtom)sendType andReturnType:(NXAtom)returnType
  133. /*
  134.  *  Services menu support.
  135.  *  We are a valid requestor if the send type is EPS and there is no 
  136.  *  return data from the request.
  137.  */
  138. {
  139.     if (image &&
  140.     ((sendType == NXPostScriptPboardType) ||
  141.      (sendType == NXTIFFPboardType)) &&
  142.     (!returnType || !*returnType))
  143.  
  144.     return self;
  145.  
  146.     return [superview validRequestorForSendType:sendType
  147.                   andReturnType:returnType];
  148. }
  149.  
  150.  
  151.  
  152. - (BOOL) writeSelectionToPasteboard:pboard types:(NXAtom *)types
  153. /*
  154.  *  Services menu support.
  155.  *  Here we are asked by the Services menu mechanism to supply
  156.  *  the image data (which we said we were a valid requestor for
  157.  *  in the above method).
  158.  */
  159. {
  160.     while (types && *types) {
  161.         if ((*types == NXPostScriptPboardType) || (*types == NXTIFFPboardType))
  162.         break;
  163.         types++;
  164.     }
  165.  
  166.     if (types && *types && [self copyToPasteboard:pboard types:types]) {
  167.         return YES;
  168.     } else {
  169.         return NO;
  170.     }
  171. }
  172.  
  173.  
  174. - (BOOL)validateCommand:menuCell
  175. {
  176.     SEL action = [menuCell action];
  177.  
  178.     if (action == @selector(copy:))
  179.         return image? YES :NO;
  180.     else if (action == @selector(copyFont:)) {
  181.     [[FontManager new] setEnabled:image? YES : NO];
  182.         return image? YES :NO;
  183.     } else if (action == @selector(pasteFont:))
  184.         return image? YES :NO;
  185.     else if (action == @selector(saveAsEPS:))
  186.     return epsStream? YES :NO;
  187.  
  188.     return YES;
  189. }
  190.  
  191.  
  192.  
  193. - copy:sender
  194. {
  195.     return [self copyToPasteboard:
  196.             [Pasteboard newName:(const char *)NXSelectionPboard]];
  197. }
  198.  
  199.  
  200.  
  201. - copyToPasteboard:pboard
  202. {
  203.     return [self copyToPasteboard:pboard types:NULL];
  204. }
  205.     
  206.  
  207.  
  208.  
  209. - copyToPasteboard:pboard types:(NXAtom *)typesList
  210. {
  211.     char *data;
  212.     NXStream *stream;   
  213.     const char *types[1];
  214.     int length, maxlen;
  215.  
  216.     if (image) {
  217.     if (!typesList || includesType(typesList, NXPostScriptPboardType))  {
  218.         types[0] = NXPostScriptPboardType;
  219.         stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  220.         [self copyPSToStream:stream];
  221.         NXGetMemoryBuffer(stream, &data, &length, &maxlen);
  222.         [pboard declareTypes:types num:1 owner:[self class]];
  223.         [pboard writeType:NXPostScriptPboardType data:data length:length];
  224.         NXCloseMemory(stream, NX_FREEBUFFER);
  225.  
  226.         return self;
  227.  
  228.     } else if (typesList && includesType(typesList, NXTIFFPboardType)) { 
  229.         types[0] = NXTIFFPboardType;
  230.         stream = NXOpenMemory(NULL, 0, NX_READWRITE);
  231.         [self copyTIFFToStream:stream];
  232.         NXGetMemoryBuffer(stream, &data, &length, &maxlen);
  233.         [pboard declareTypes:types num:1 owner:[self class]];
  234.         [pboard writeType:NXTIFFPboardType data:data length:length];
  235.         NXCloseMemory(stream, NX_FREEBUFFER);
  236.  
  237.         return self;
  238.  
  239.     }
  240.     }
  241.  
  242.     return nil;
  243. }
  244.  
  245.  
  246. - copyPSToStream:(NXStream *)stream
  247. {
  248.     char *streambuf;
  249.     int len, maxlen;
  250.  
  251.     if (image) {
  252.  
  253.     NXGetMemoryBuffer(epsStream, &streambuf, &len, &maxlen);
  254.     NXPrintf (stream, "%s", streambuf);
  255.  
  256.     return self;
  257.  
  258.     }
  259.  
  260.     return nil;
  261. }
  262.  
  263.  
  264.  
  265. - copyTIFFToStream:(NXStream *)stream
  266. {
  267.     if (image) {
  268.     [image writeTIFF:stream];
  269.     return self;
  270.     }
  271.  
  272.     return nil;
  273. }
  274.  
  275.  
  276.  
  277. - saveAsEPS:sender
  278. {
  279.     id savePanel;
  280.  
  281.     savePanel = [SavePanel new];
  282.     [savePanel setRequiredFileType:"eps"];
  283.     [savePanel runModal];
  284.     [self saveEPSToFile:[savePanel filename]];
  285.  
  286.     return self;
  287. }
  288.  
  289.  
  290.  
  291. - (const char *) writeEPSToTempFile
  292. {
  293.     const char *realName;
  294.     char *cur;
  295.     int pos;
  296.  
  297.     /*  
  298.      *  If epsStream has been written to a temp file before, and the 
  299.      *  epsStream hasn't changed since then, we don't need to write 
  300.      *  the temp file again.  This checks to see if the stream has 
  301.      *  changed.  It also should (but doesn't) check to see if the 
  302.      *  temp file still exists and is accessable (readable).
  303.      */
  304.     if (!tempFileNeedsUpdate)
  305.     return fullPath;
  306.     else if (!epsStream) {
  307.     [self unlinkFile];
  308.     return NULL;
  309.     }
  310.  
  311.     [self unlinkFile];        /* Get rid of previous temp file, if any */
  312.     fullPath = (char *) NXZoneMalloc ([self zone], MAXPATHLEN * sizeof (char));
  313.     *fullPath = '\0';
  314.  
  315.     /*  
  316.      *  We need to come up with a unique name for the temp file, which 
  317.      *  will reside in /tmp.  We use the name of the plot and change 
  318.      *  it with NXGetTempFilename.
  319.      */
  320.     realName = [[window delegate] name];
  321.     if (cur = rindex(realName, '/')) {    /* The plot has been titled    */
  322.  
  323.     if (++cur)
  324.         sprintf (fullPath, "/tmp/%s", cur);
  325.     else
  326.         return NULL;        /* ...ERROR: path ends in slash    */
  327.  
  328.     } else                /* The plot is untitled        */
  329.  
  330.     sprintf (fullPath, "/tmp/%s", realName);
  331.  
  332.  
  333.     pos = strlen (fullPath);
  334.  
  335.     if (cur = rindex (fullPath, '.')) {    /* The name has an extension    */
  336.     pos -= strlen (cur);        /* Replace it with .eps        */
  337.     strcpy (cur, "xxxxxx.eps");
  338.     } else                /* The name has no extension    */
  339.     strcat (fullPath, "xxxxxx.eps");
  340.  
  341.  
  342.     NXGetTempFilename(fullPath, pos);    /* Make sure filename is unique    */
  343.     [self saveEPSToFile:fullPath];    /* Save the stream to the file    */
  344.  
  345.     tempFileNeedsUpdate = NO;
  346.     return fullPath;
  347. }
  348.  
  349.  
  350. - saveEPSToFile:(const char *)filename
  351. {
  352.     /*  
  353.      *  Saves the epsStream to a file as Encapsulated PostScript.  
  354.      *  Assumes filename is a validly formed, unique pathname.  (It 
  355.      *  will overwrite the file if filename is not unique.)
  356.      */
  357.     
  358.     NXStream *newStream;
  359.     char *buf;
  360.     int bufsize, bufmax;
  361.     
  362. // See Status.m for description of problem with NXSaveToFile().
  363. //    NXSaveToFile (epsStream, filename);
  364.  
  365.     newStream = NXOpenMemory (NULL, 0, NX_WRITEONLY);
  366.     NXGetMemoryBuffer (epsStream, &buf, &bufsize, &bufmax);
  367.     NXPrintf (newStream, "%s", buf);
  368.     NXFlush (newStream);
  369.     NXSaveToFile (newStream, filename);
  370.     NXCloseMemory (newStream, NX_FREEBUFFER);
  371.  
  372.     return self;
  373. }
  374.  
  375.  
  376.  
  377. - unlinkFile
  378. {
  379.     if (fullPath)  {
  380.     if (*fullPath)
  381.         unlink (fullPath);
  382.     NXZoneFree ([self zone], fullPath);
  383.     fullPath = NULL;
  384.     }
  385.  
  386.     return self;
  387. }
  388.  
  389.  
  390.  
  391. - mouseDown:(NXEvent *)theEvent
  392. {
  393.     NXRect    fileRect = {{theEvent->location.x - 24.0,
  394.                  theEvent->location.y - 24.0}, {48.0, 48.0}};
  395.  
  396.     if ([self writeEPSToTempFile])  {
  397.     [[[window contentView] superview] convertRect:&fileRect toView:self];
  398.  
  399.     [self dragFile:fullPath
  400.               fromRect:&fileRect
  401.              slideBack:YES
  402.                  event:theEvent];
  403.     
  404.     return self;
  405.     } else
  406.     return nil;
  407. }
  408.  
  409.  
  410.         
  411. /*
  412.  *  Redisplays the contents of the view by compositing the image onto 
  413.  *  the view.
  414.  */
  415. - drawSelf:(NXRect *)r :(int) count
  416. {
  417.     PSsetgray(NX_WHITE);    /* For color machines, we must clear    */
  418.     NXRectFill(&(r[0]));    /* the window to white.            */
  419.  
  420.     if (image)
  421.     [image composite:NX_SOVER fromRect:&(r[0]) toPoint:&(r[0].origin)];
  422.  
  423.     return self;
  424. }
  425.  
  426.  
  427. - newStream:(NXStream *) aStream
  428. {
  429.     NXSize imageSize;
  430.  
  431.     if (image)
  432.     [image free];
  433.  
  434.     if (aStream)  {
  435.  
  436.     NXSeek(aStream, 0, NX_FROMSTART);
  437.     image = [[[[NXImage allocFromZone: [self zone]]
  438.                initFromStream: aStream]
  439.                   setScalable: YES]
  440.               setDataRetained: YES];
  441.  
  442.     [image getSize:&imageSize];
  443.     imageOrigWidth = imageSize.width;
  444.     imageOrigHeight = imageSize.height;
  445.  
  446.     NX_WIDTH(&bounds) = NX_WIDTH(&frame) = imageSize.width;
  447.     NX_HEIGHT(&bounds) = NX_HEIGHT(&frame) = imageSize.height;
  448.     epsStream = aStream;
  449.  
  450.     } else {
  451.     NX_WIDTH(&bounds) = NX_HEIGHT(&bounds) = NX_WIDTH(&frame) =
  452.         NX_HEIGHT(&frame) = 0;
  453.     imageOrigWidth = imageOrigHeight = 0;
  454.     image = nil;
  455.     epsStream = NULL;
  456.     }
  457.  
  458.     tempFileNeedsUpdate = YES;
  459.  
  460.     return self;
  461. }
  462.  
  463.  
  464.  
  465.  
  466.  
  467. /*  
  468.  *  This method, in addition to freeing the image, unlinks the 
  469.  *  temporary EPS file if it exists.
  470.  */
  471. - free
  472. {
  473.     [self unlinkFile];
  474.  
  475.     if (image)
  476.     [image free];
  477.  
  478.     return [super free];
  479. }
  480.  
  481.  
  482. /*
  483.  *  This method takes only one scale factor because we never want the 
  484.  *  view to have different proportions than the image.  Scale factor 
  485.  *  of zero means size to window (so that the entire plot is visible 
  486.  *  in the window).
  487.  */
  488. - (float)scale:(float)scaleFactor
  489. {
  490.     float    factor;
  491.     NXSize    imageSize;
  492.     NXRect    ourRect;
  493.  
  494.     if (image)  {
  495.  
  496.     if (scaleFactor)  {
  497.  
  498.         factor = scaleFactor;
  499.  
  500.     } else  {
  501.  
  502.         [superview getDocVisibleRect: &ourRect];
  503.         factor = ourRect.size.width / imageOrigWidth;
  504.         if ((imageOrigHeight * factor) > ourRect.size.height)
  505.         factor = ourRect.size.height / imageOrigHeight;
  506.  
  507.     }
  508.  
  509.     imageSize.width = imageOrigWidth * factor;
  510.     imageSize.height = imageOrigHeight * factor;
  511.     [image setSize:&imageSize];
  512.  
  513.     /* Show the newly sized image. */
  514.  
  515.     NX_WIDTH(&bounds) = NX_WIDTH(&frame) = imageSize.width;
  516.     NX_HEIGHT(&bounds) = NX_HEIGHT(&frame) = imageSize.height;
  517.     [superview descendantFrameChanged:self];
  518.  
  519.     [self display];
  520.     return factor;
  521.  
  522.     } else
  523.     return 0.0;
  524. }
  525.  
  526.  
  527. // Shuts up the compiler about unused RCSId
  528. - (const char *) rcsid
  529. {
  530.     return RCSId;
  531. }
  532.  
  533.  
  534. @end
  535.